package com.empire.module.member.service.warninggather;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.empire.framework.common.util.date.LocalDateTimeUtils;
import com.empire.module.member.controller.admin.location.vo.LocationSaveReqVO;
import com.empire.module.member.controller.admin.locationlast.vo.LocationLastSaveReqVO;
import com.empire.module.member.controller.admin.mappolygonpoint.vo.PointInPolygonReqVO;
import com.empire.module.member.controller.admin.warninggather.vo.*;
import com.empire.module.member.dal.dataobject.mapconfig.MapConfigDO;
import com.empire.module.member.dal.dataobject.user.MemberUserDO;
import com.empire.module.member.dal.dataobject.warninggather.WarningGatherDO;
import com.empire.module.member.dal.dataobject.warninggatherperson.WarningGatherPersonDO;
import com.empire.module.member.dal.mysql.mapconfig.MapConfigMapper;
import com.empire.module.member.dal.mysql.user.MemberUserMapper;
import com.empire.module.member.dal.mysql.warninggather.WarningGatherMapper;
import com.empire.module.member.dal.mysql.warninggatherperson.WarningGatherPersonMapper;
import com.empire.framework.common.pojo.PageResult;
import com.empire.framework.common.util.object.BeanUtils;
import com.empire.module.member.service.location.LocationService;
import com.empire.module.member.service.locationlast.LocationLastService;
import com.empire.module.member.service.mapconfig.MapConfigService;
import com.empire.module.member.service.mappointInarea.PointInPolygonService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.empire.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.empire.module.member.enums.ErrorCodeConstants.*;

/**
 * 聚集预警主表（1条记录对应1次聚集事件） Service 实现类
 */
@Service
@Validated
public class WarningGatherServiceImpl implements WarningGatherService {
    private static final Logger log = LoggerFactory.getLogger(WarningGatherServiceImpl.class);

    @Resource
    private WarningGatherMapper warningGatherMapper;
    @Resource
    private WarningGatherPersonMapper warningGatherPersonMapper;
    @Resource
    private MapConfigMapper mapConfigMapper;
    @Resource
    private MemberUserMapper memberUserMapper;
    @Resource
    private PointInPolygonService pointInPolygonService;
    @Resource
    private LocationService locationService;
    @Resource
    private LocationLastService locationLastService;
    // 新增：注入抽离的聚集保存服务
    @Resource
    private GatherPersonSaveService gatherPersonSaveService;
    @Resource
    private MapConfigService mapConfigService;

    // 日期时间格式化器
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    // 常量定义：最小聚集规模
    private static final int MIN_GATHER_SIZE = 2;

    // 维护当前活跃的聚集，key为配置ID，value为该配置下的活跃聚集列表
    private final Map<String, List<ActiveGather>> activeGathers = new ConcurrentHashMap<>();

    // ------------------------------ 原有方法保留（删除原saveOrUpdateGather） ------------------------------
    @Override
    public Long createWarningGather(WarningGatherSaveReqVO createReqVO) {
        WarningGatherDO warningGather = BeanUtils.toBean(createReqVO, WarningGatherDO.class);
        warningGatherMapper.insert(warningGather);
        return warningGather.getId();
    }

    @Override
    public void updateWarningGather(WarningGatherSaveReqVO updateReqVO) {
        validateWarningGatherExists(updateReqVO.getId());
        WarningGatherDO updateObj = BeanUtils.toBean(updateReqVO, WarningGatherDO.class);
        warningGatherMapper.updateById(updateObj);
    }

    @Override
    public void deleteWarningGather(Long id) {
        validateWarningGatherExists(id);
        warningGatherPersonMapper.deleteByGatherId(id);
        warningGatherMapper.deleteById(id);
    }

    private void validateWarningGatherExists(Long id) {
        if (warningGatherMapper.selectById(id) == null) {
            throw exception(WARNING_GATHER_NOT_EXISTS);
        }
    }

    @Override
    public WarningGatherDO getWarningGather(Long id) {
        return warningGatherMapper.selectById(id);
    }

    @Override
    public PageResult<WarningGatherDO> getWarningGatherPage(WarningGatherPageReqVO pageReqVO) {
        List<Long> personIdList = memberUserMapper.selectByIdNumber(pageReqVO.getIdNumber());
        if(personIdList != null && !personIdList.isEmpty()){
            Long personId = personIdList.getFirst();
            LambdaQueryWrapper<WarningGatherPersonDO> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(WarningGatherPersonDO::getPersonId, personId);
            List<WarningGatherPersonDO> warningGatherPersonDOS = warningGatherPersonMapper.selectList(queryWrapper);
            Set<Long> gatherIdSet = warningGatherPersonDOS.stream().map(WarningGatherPersonDO::getGatherId).collect(Collectors.toSet());
            pageReqVO.setGatherIdList(gatherIdSet);
        }
        return warningGatherMapper.selectPage(pageReqVO);
    }

    public List<MapConfigDO> getConfigSetting(Integer warnType) {
        LambdaQueryWrapper<MapConfigDO> query = new LambdaQueryWrapper<>();
        query.eq(MapConfigDO::getWarnType, warnType);
        List<MapConfigDO> configList = mapConfigMapper.selectList(query);
        if (configList.isEmpty()) {
            log.warn("未加载到任何聚集配置");
        } else {
            log.debug("初始化加载到{}个聚集配置", configList.size());
        }
        return configList;
    }

//    @KafkaListener(topics = "datahub-location-topic", groupId = "warning-gather-group")
    public void receiveDataHubMessage(String message) {
        try {
            // 解析DataHub消息（注意：此处ActiveGather需改为ActiveGather）
            ActiveGather.DataHubLocationVO dataHubVO =
                    JSONObject.parseObject(message, ActiveGather.DataHubLocationVO.class);
            log.debug("接收到DataHub数据: idNumber={}, time={}",
                    dataHubVO.getIdNumber(), dataHubVO.getLocateTime());

            // 转换为内部LocationPointVO（改为LocationPointVO）
            LocationPointVO point = convertToLocationPoint(dataHubVO);
            if (point == null) {
                log.error("数据转换失败: {}", message);
                return;
            }

            // 入库定位表
            processLocation(point);

            // 处理围栏逻辑
            PointInPolygonReqVO reqVO = new PointInPolygonReqVO();
            BeanUtils.copyProperties(point, reqVO);
            point.setIsCurrent(true);
            pointInPolygonService.judgePointInPolygon(reqVO);

            // 处理聚集逻辑
//            processReceivedGatherPoint(point);

        } catch (Exception e) {
            log.error("处理DataHub消息异常", e);
        }
    }

    private void processLocation(LocationPointVO point) {
        try {
            LocationSaveReqVO location = new LocationSaveReqVO();
            BeanUtils.copyProperties(point, location);
            if (StrUtil.isNotEmpty(point.getLocateTime())) {
                location.setLocationTime(LocalDateTimeUtils.parseLongTime(point.getLocateTime()));
            }
            locationService.createLocation(location);
            LocationLastSaveReqVO locationLast = new LocationLastSaveReqVO();
            BeanUtils.copyProperties(point, locationLast);
            if (StrUtil.isNotEmpty(point.getLocateTime())) {
                locationLast.setLocationTime(LocalDateTimeUtils.parseLongTime(point.getLocateTime()));
            }
            locationLastService.createLocationLast(locationLast);
        }catch (Exception e){
            log.warn("插入数据重复-{}", e.getMessage());
        }
    }

    private LocationPointVO convertToLocationPoint(ActiveGather.DataHubLocationVO dataHubVO) {
        try {
            LocationPointVO point = new LocationPointVO();
            Long personId = null;
            if (dataHubVO.getIdType() != null && dataHubVO.getIdType() == 1 && StrUtil.isNotEmpty(dataHubVO.getIdNumber())) {
                MemberUserDO memberUserDO = memberUserMapper.selectByIdcard(dataHubVO.getIdNumber());
                if (memberUserDO != null) {
                    personId = memberUserDO.getId();
                }
            }
            if (dataHubVO.getIdType() != null && dataHubVO.getIdType() == 2 && StrUtil.isNotEmpty(dataHubVO.getIdNumber())) {
                MemberUserDO memberUserDO = memberUserMapper.selectByPassportNumber(dataHubVO.getIdNumber());
                if (memberUserDO != null) {
                    personId = memberUserDO.getId();
                }
            }
            if (personId != null) {
                point.setPersonId(personId);
                point.setLocateTime(dataHubVO.getLocateTime());
                point.setLng(dataHubVO.getLng());
                point.setLat(dataHubVO.getLat());
                point.setAddress(dataHubVO.getAddress());
                return point;
            }
            return null;

        } catch (Exception e) {
            log.error("数据格式转换失败", e);
            return null;
        }
    }

    // 处理聚集点逻辑（ActiveGather改为ActiveGather）
    private void processReceivedGatherPoint(LocationPointVO point) {
        List<MapConfigDO> configList = getConfigSetting(2);
        if (configList == null || configList.isEmpty()) {
            log.warn("无聚集配置，跳过处理点: {}", point.getPersonId());
            return;
        }

        for (MapConfigDO configDO : configList) {
            if (isPointInTimeRange(point, configDO.getStartTime(), configDO.getEndTime())) {
                log.debug("点{}在配置[{}]的时间范围内，开始处理聚集",
                        point.getPersonId(), configDO.getId());
                List<WarningGatherResultVO> results = processNewPoint(configDO, point);
                if (!results.isEmpty()) {
                    log.debug("点{}处理完成，生成{}个聚集结果",
                            point.getPersonId(), results.size());
                }
            } else {
                log.debug("点{}不在配置[{}]的时间范围内，跳过处理",
                        point.getPersonId(), configDO.getId());
            }
        }
    }

    // 核心处理新点方法（ActiveGather改为ActiveGather）
    public List<WarningGatherResultVO> processNewPoint(MapConfigDO config, LocationPointVO newPoint) {
        List<WarningGatherResultVO> results = new ArrayList<>();

        if (config.getDistance() == null || config.getInterval() == null) {
            throw exception(WARNING_GATHER_CONFIG_INCOMPLETE);
        }

        String configId = config.getId();
        activeGathers.putIfAbsent(configId, new ArrayList<>());
        List<ActiveGather> gathers = activeGathers.get(configId);

        // 打印当前活跃聚集状态
        logActiveGathers(configId, "处理点[" + newPoint.getPersonId() + "]前的活跃聚集");

        // 查找所有可匹配的聚集（包括间接关联的）
        List<ActiveGather> matchingGathers = findAllRelatedGathers(gathers, newPoint, config.getDistance(), config.getInterval());
        log.debug("点{}找到{}个匹配的聚集", newPoint.getPersonId(), matchingGathers.size());
        matchingGathers.forEach(gather -> log.debug("匹配的聚集包含人员: {}",
                gather.getPoints().stream().map(p -> p.getPersonId().toString()).collect(Collectors.joining(","))));

        ActiveGather targetGather = new ActiveGather();
        targetGather.addPoint(newPoint);
        log.debug("创建新的目标聚集，初始包含点: {}", newPoint.getPersonId());

        // 合并所有匹配的聚集
        if (!matchingGathers.isEmpty()) {
            for (ActiveGather gather : matchingGathers) {
                log.debug("合并聚集（包含人员: {}）到目标聚集",
                        gather.getPoints().stream().map(p -> p.getPersonId().toString()).collect(Collectors.joining(",")));
                targetGather.merge(gather);
                gathers.remove(gather);
            }
        } else {
            // 查找附近单点
            List<LocationPointVO> nearbySinglePoints = findNearbySinglePoints(gathers, newPoint, config.getDistance(), config.getInterval());
            if (!nearbySinglePoints.isEmpty()) {
                log.debug("找到{}个附近单点，将其合并到目标聚集: {}",
                        nearbySinglePoints.size(),
                        nearbySinglePoints.stream().map(p -> p.getPersonId().toString()).collect(Collectors.joining(",")));
                nearbySinglePoints.forEach(targetPoint -> {
                    targetGather.addPoint(targetPoint);
                });
                removeSinglePoints(gathers, nearbySinglePoints);
            } else {
                log.debug("未找到匹配的聚集和附近单点，目标聚集仅包含当前点");
            }
        }

        // 添加新聚集到列表
        gathers.add(targetGather);
        log.debug("目标聚集添加到活跃列表，当前包含人员: {}",
                targetGather.getPoints().stream().map(p -> p.getPersonId().toString()).collect(Collectors.joining(",")));

        // 强制进行全量合并检查，确保所有相关聚集都被合并
        log.debug("开始执行全量合并检查...");
        mergeAllRelatedGathers(gathers, config.getDistance(), config.getInterval());
        logActiveGathers(configId, "全量合并检查后的活跃聚集");

        // 生成结果：调用抽离的gatherPersonSaveService保存聚集（关键修改）
        if (targetGather.getPoints().size() >= MIN_GATHER_SIZE) {
            WarningGatherResultVO result = convertToWarningResult(targetGather);
            results.add(result);
            gatherPersonSaveService.saveOrUpdateGather(targetGather, config);
            log.debug("生成聚集结果，包含人员: {}",
                    targetGather.getPoints().stream().map(p -> p.getPersonId().toString()).collect(Collectors.joining(",")));
        }

        return results;
    }

    // ------------------------------ 原有辅助方法调整（适配ActiveGather包路径） ------------------------------
    private void logActiveGathers(String configId, String stage) {
        List<ActiveGather> gathers = activeGathers.getOrDefault(configId, new ArrayList<>());
        log.debug("===== {} (共{}个) =====", stage, gathers.size());
        for (int i = 0; i < gathers.size(); i++) {
            ActiveGather gather = gathers.get(i);
            String personIds = gather.getPoints().stream()
                    .map(p -> p.getPersonId().toString())
                    .collect(Collectors.joining(","));
            log.debug("聚集{}: 包含{}个点，人员ID: [{}]",
                    i + 1, gather.getPoints().size(), personIds);
        }
        log.debug("==========================");
    }

    private List<ActiveGather> findAllRelatedGathers(List<ActiveGather> gathers, LocationPointVO newPoint,
                                                                             double maxDistance, Integer maxIntervalMinutes) {
        Set<ActiveGather> relatedGathers = new HashSet<>();
        Queue<ActiveGather> queue = new LinkedList<>();

        // 先找到直接匹配的聚集
        List<ActiveGather> directMatches = gathers.stream()
                .filter(gather -> !gather.getPoints().isEmpty())
                .filter(gather -> isPointMatchingGather(gather, newPoint, maxDistance, maxIntervalMinutes))
                .toList();

        relatedGathers.addAll(directMatches);
        queue.addAll(directMatches);

        // 再通过BFS找到间接关联的聚集
        while (!queue.isEmpty()) {
            ActiveGather current = queue.poll();
            for (ActiveGather gather : gathers) {
                if (!relatedGathers.contains(gather) && gather != current &&
                        isGatherMatchingGather(current, gather, maxDistance, maxIntervalMinutes)) {
                    relatedGathers.add(gather);
                    queue.add(gather);
                }
            }
        }

        return new ArrayList<>(relatedGathers);
    }

    private void mergeAllRelatedGathers(List<ActiveGather> gathers, double maxDistance, Integer maxIntervalMinutes) {
        boolean hasMerged;
        int mergeCount = 0;
        do {
            hasMerged = false;
            // 遍历所有可能的聚集对
            for (int i = 0; i < gathers.size(); i++) {
                for (int j = i + 1; j < gathers.size(); j++) {
                    ActiveGather gather1 = gathers.get(i);
                    ActiveGather gather2 = gathers.get(j);

                    if (isGatherMatchingGather(gather1, gather2, maxDistance, maxIntervalMinutes)) {
                        // 合并两个聚集
                        gather1.merge(gather2);
                        gathers.remove(j);
                        hasMerged = true;
                        mergeCount++;
                        // 合并后需要重新检查，跳出当前循环
                        i = gathers.size(); // 退出外层循环
                        break;
                    }
                }
            }
        } while (hasMerged);

        log.debug("全量合并检查完成，共合并了{}对聚集", mergeCount);
    }

    private boolean isGatherMatchingGather(ActiveGather gather1, ActiveGather gather2,
                                           double maxDistance, Integer maxIntervalMinutes) {
        for (LocationPointVO p1 : gather1.getPoints()) {
            for (LocationPointVO p2 : gather2.getPoints()) {
                double distance = calculateDistance(p1.getLng(), p1.getLat(), p2.getLng(), p2.getLat());
                boolean distanceMatch = distance <= maxDistance;

                long minutesBetween = 0;
                try {
                    LocalDateTime dt1 = LocalDateTime.parse(p1.getLocateTime(), DATE_TIME_FORMATTER);
                    LocalDateTime dt2 = LocalDateTime.parse(p2.getLocateTime(), DATE_TIME_FORMATTER);
                    minutesBetween = Math.abs(Duration.between(dt1, dt2).toMinutes());
                } catch (Exception e) {
                    log.error("计算时间间隔异常", e);
                }
                boolean timeMatch = minutesBetween <= maxIntervalMinutes;

                if (distanceMatch && timeMatch) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isPointMatchingGather(ActiveGather gather, LocationPointVO point,
                                          double maxDistance, Integer maxIntervalMinutes) {
        return gather.getPoints().stream()
                .anyMatch(p -> {
                    double distance = calculateDistance(p.getLng(), p.getLat(), point.getLng(), point.getLat());
                    boolean distanceMatch = distance <= maxDistance;

                    long minutesBetween = 0;
                    try {
                        LocalDateTime dt1 = LocalDateTime.parse(p.getLocateTime(), DATE_TIME_FORMATTER);
                        LocalDateTime dt2 = LocalDateTime.parse(point.getLocateTime(), DATE_TIME_FORMATTER);
                        minutesBetween = Math.abs(Duration.between(dt1, dt2).toMinutes());
                    } catch (Exception e) {
                        log.error("计算时间间隔异常", e);
                    }
                    boolean timeMatch = minutesBetween <= maxIntervalMinutes;

                    return distanceMatch && timeMatch;
                });
    }

    private List<LocationPointVO> findNearbySinglePoints(List<ActiveGather> gathers, LocationPointVO newPoint,
                                                                                 double maxDistance, Integer maxIntervalMinutes) {
        return gathers.stream()
                .filter(gather -> gather.getPoints().size() == 1)
                .map(gather -> gather.getPoints().get(0))
                .filter(p -> {
                    double distance = calculateDistance(p.getLng(), p.getLat(), newPoint.getLng(), newPoint.getLat());
                    boolean distanceMatch = distance <= maxDistance;

                    long minutesBetween = 0;
                    try {
                        LocalDateTime dt1 = LocalDateTime.parse(p.getLocateTime(), DATE_TIME_FORMATTER);
                        LocalDateTime dt2 = LocalDateTime.parse(newPoint.getLocateTime(), DATE_TIME_FORMATTER);
                        minutesBetween = Math.abs(Duration.between(dt1, dt2).toMinutes());
                    } catch (Exception e) {
                        log.error("计算时间间隔异常", e);
                    }
                    boolean timeMatch = minutesBetween <= maxIntervalMinutes;

                    return distanceMatch && timeMatch;
                })
                .collect(Collectors.toList());
    }

    private void removeSinglePoints(List<ActiveGather> gathers, List<LocationPointVO> pointsToRemove) {
        Set<Long> personIds = pointsToRemove.stream()
                .map(LocationPointVO::getPersonId)
                .collect(Collectors.toSet());

        int removedCount = 0;
        Iterator<ActiveGather> iterator = gathers.iterator();
        while (iterator.hasNext()) {
            ActiveGather gather = iterator.next();
            if (gather.getPoints().size() == 1 &&
                    personIds.contains(gather.getPoints().get(0).getPersonId())) {
                iterator.remove();
                removedCount++;
            }
        }
        log.debug("从活跃聚集列表中移除了{}个单点聚集", removedCount);
    }

    private WarningGatherResultVO convertToWarningResult(ActiveGather gather) {
        List<LocationPointVO> points = gather.getPoints();
        List<LocalDateTime> times = points.stream()
                .map(point -> LocalDateTime.parse(point.getLocateTime(), DATE_TIME_FORMATTER))
                .sorted()
                .toList();

        LocalDateTime startTime = times.getFirst();
        LocalDateTime endTime = times.getLast();
        Set<Long> personIds = points.stream()
                .map(LocationPointVO::getPersonId)
                .collect(Collectors.toSet());

        WarningGatherResultVO result = new WarningGatherResultVO();
        result.setGatherId(gather.getId());
        result.setGatherStartTime(startTime.format(DATE_TIME_FORMATTER));
        result.setGatherEndTime(endTime.format(DATE_TIME_FORMATTER));
        result.setTotalPersons(personIds.size());
        result.setPersons(new ArrayList<>(points)); // 若VO不兼容需手动转换，此处假设字段一致

        return result;
    }

    private boolean isPointInTimeRange(LocationPointVO point, Date startTime, Date endTime) {
        try {
            Date locateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(point.getLocateTime());
            return isTimeInRange(locateTime, startTime, endTime);
        } catch (ParseException e) {
            log.error("解析点时间异常", e);
            return false;
        }
    }

    private boolean isTimeInRange(Date targetTime, Date startTime, Date endTime) {
        if (startTime == null || endTime == null || targetTime == null) {
            return false;
        }

        int targetYear = getYear(targetTime);
        int startYear = getYear(startTime);
        int endYear = getYear(endTime);

        if (startYear == endYear) {
            if (startTime.before(endTime) || startTime.equals(endTime)) {
                return !targetTime.before(startTime) && !targetTime.after(endTime);
            } else {
                return !targetTime.before(startTime) || !targetTime.after(endTime);
            }
        } else {
            if (targetYear == startYear) {
                return !targetTime.before(startTime);
            } else if (targetYear == endYear) {
                return !targetTime.after(endTime);
            } else if (targetYear > startYear && targetYear < endYear) {
                return true;
            } else {
                return false;
            }
        }
    }

    private int getYear(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar.get(Calendar.YEAR);
    }

    private double calculateDistance(double lng1, double lat1, double lng2, double lat2) {
        final int R = 6371000; // 地球半径（米）

        double latDistance = Math.toRadians(lat2 - lat1);
        double lonDistance = Math.toRadians(lng2 - lng1);

        double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
                * Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        return R * c;
    }
}